# -*- coding: utf-8 -*-
"""
 @Time    : 2021/1/19 下午5:48
 @Author  : tgy
 
"""

class Person:

    __slots__ = ['_name','_age']

    def __init__(self, name, age):
        ...
        self._name = name
        self._age = age

    def get_name(self):
        return self._name

    def __str__(self):
        return 'per[name={name},age={age}]'.format(name=self._name,age=self._age)

    def __repr__(self):
        return str(self)

class attrgetter:
    """
    Return a callable object that fetches the given attribute(s) from its operand.
    After f = attrgetter('name'), the call f(r) returns r.name.
    After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
    After h = attrgetter('name.first', 'name.last'), the call h(r) returns
    (r.name.first, r.name.last).
    """
    __slots__ = ('_attrs', '_call')

    def __init__(self, attr, *attrs):
        if not attrs:
            if not isinstance(attr, str):
                raise TypeError('attribute name must be a string')
            self._attrs = (attr,)
            names = attr.split('.')
            def func(obj):
                for name in names:
                    obj = getattr(obj, name)
                return obj
            self._call = func
        else:
            self._attrs = (attr,) + attrs
            getters = tuple(map(attrgetter, self._attrs))
            def func(obj):
                return tuple(getter(obj) for getter in getters)
            self._call = func

    def __call__(self, obj):
        return self._call(obj)

    def __repr__(self):
        return '%s.%s(%s)' % (self.__class__.__module__,
                              self.__class__.__qualname__,
                              ', '.join(map(repr, self._attrs)))

    def __reduce__(self):
        return self.__class__, self._attrs



if __name__ == '__main__':
    ...
    pers = [Person('tony', 21), Person('Mac', 24)]
    pers.sort(key=attrgetter('_age', '_name'))
